﻿namespace H2.Streams.Sections
{
    using H2;
    using H2.DataTypes;
    using H2.Streams;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using System.Windows.Forms;

    public class Tags
    {
        internal List<string> ClassCache;
        private MapStream Map;
        internal List<string> PathCache;
        internal List<TagInfo> TagInfoCache;
        internal Dictionary<string, System.Type> TagStructureTypeDic;

        internal Tags(MapStream map)
        {
            this.Map = map;
            this.CreateStructureCache();
            this.CreateTagCache();
        }

        public void Add(TagContainer tagCon, string Class, string Path)
        {
            object tag = tagCon.Tag;
            object rawData = tagCon.RawData;
            TagInfo info = new TagInfo();
            int num = 0x10;
            int num2 = Path.Length + 1;
            int num3 = 4;
            int size = this.GetSize(tag, true);
            int num5 = 0;
            int dataSize = 0;
            int metaStart = this.Map.Header.MetaStart;
            for (int i = 0; i < this.TagInfoCache.Count; i++)
            {
                int num9 = (this.TagInfoCache[i].Offset + this.TagInfoCache[i].Size) - metaStart;
                if (num9 > dataSize)
                {
                    dataSize = num9;
                }
            }
            int fileTableSize = this.Map.Header.FileTableSize;
            int num11 = this.Map.CalculatePaddingSize(fileTableSize, 0x200);
            int num12 = this.Map.CalculatePaddingSize(fileTableSize + num2, 0x200) - num11;
            num11 += num12;
            int num13 = this.Map.Header.FileTableCount * num3;
            int num14 = this.Map.CalculatePaddingSize(num13, 0x200);
            int num15 = this.Map.CalculatePaddingSize(num13 + num3, 0x200) - num14;
            num14 += num15;
            int num16 = this.Map.CalculatePaddingSize(dataSize, 0x200);
            int num17 = this.Map.CalculatePaddingSize(dataSize + size, 0x200) - num16;
            num16 += num17;
            BitmapCollection.BitmapDataChunk[] chunkArray = null;
            if (tag.GetType() == typeof(TagStructures.bitm))
            {
                chunkArray = (BitmapCollection.BitmapDataChunk[]) rawData;
                for (int k = 0; k < chunkArray.Length; k++)
                {
                    num5 += (chunkArray[k].LOD1.Length + chunkArray[k].LOD2.Length) + chunkArray[k].LOD3.Length;
                }
            }
            int num19 = ((num2 + num3) + num12) + num15;
            info.Size = size;
            info.ID = this.GetUnusedID();
            TagInfo[] tagInfoArrayOfClass = this.Map.Tags.GetTagInfoArrayOfClass("bitm");
            this.Map.Tags.GetStringArrayOfClass("bitm");
            int num20 = 0;
            this.Map.Status = "Fixing Bitmap Raw...";
            for (int j = 0; j < tagInfoArrayOfClass.Length; j++)
            {
                Application.DoEvents();
                this.Map.Position = tagInfoArrayOfClass[j].Offset + 0x44;
                int num22 = this.Map.ReadInt32();
                int num23 = this.Map.ReadInt32() - this.Map.SecondaryMagic;
                for (int m = 0; m < num22; m++)
                {
                    int num25 = num23 + (0x74 * m);
                    this.Map.Position = num25 + 0x1c;
                    int num26 = this.Map.ReadInt32();
                    if (num26 == -1)
                    {
                        break;
                    }
                    if ((num26 & 0xc0000000L) == 0L)
                    {
                        int num27 = this.Map.ReadInt32();
                        int num28 = this.Map.ReadInt32();
                        this.Map.Position = num25 + 0x1c;
                        this.Map.Write((int) (num26 + num19));
                        if (num27 != -1)
                        {
                            this.Map.Write((int) (num27 + num19));
                        }
                        else
                        {
                            this.Map.Position += 4L;
                        }
                        if (num28 != -1)
                        {
                            this.Map.Write((int) (num28 + num19));
                        }
                    }
                }
            }
            int indexOffset = this.Map.IndexOffset;
            if (tag.GetType() == typeof(TagStructures.bitm))
            {
                TagStructures.bitm bitm = (TagStructures.bitm) tag;
                int num30 = indexOffset + num19;
                int[] numArray = new int[bitm._68_Bitmap_Data.Length];
                for (int n = 0; n < bitm._68_Bitmap_Data.Length; n++)
                {
                    int num32 = bitm._68_Bitmap_Data[n]._28_LOD1_Offset;
                    if (num32 == -1)
                    {
                        break;
                    }
                    if ((num32 & 0xc0000000L) == 0L)
                    {
                        if (chunkArray[n].LOD1.Length > 0)
                        {
                            bitm._68_Bitmap_Data[n]._28_LOD1_Offset = num30;
                            num30 += chunkArray[n].LOD1.Length;
                        }
                        if (chunkArray[n].LOD2.Length > 0)
                        {
                            bitm._68_Bitmap_Data[n]._32_LOD2_Offset = num30;
                            num30 += chunkArray[n].LOD2.Length;
                        }
                        if (chunkArray[n].LOD3.Length > 0)
                        {
                            bitm._68_Bitmap_Data[n]._36_LOD3_Offset = num30;
                            num30 += chunkArray[n].LOD3.Length;
                        }
                        numArray[n] = this.Map.CalculatePaddingSize((chunkArray[n].LOD1.Length + chunkArray[n].LOD2.Length) + chunkArray[n].LOD3.Length, 0x200);
                        num20 += numArray[n];
                        num30 += numArray[n];
                    }
                }
                this.Map.Stream.SetLength((this.Map.Length + num5) + num20);
                this.Map.WriteAt((indexOffset + num5) + num20, this.Map.ReadBytesAt(indexOffset, ((int) this.Map.Length) - indexOffset, true), true);
                Header header1 = this.Map.Header;
                header1.IndexOffset += num5 + num20;
                this.Map.Position = indexOffset;
                for (int num33 = 0; num33 < chunkArray.Length; num33++)
                {
                    this.Map.Write(chunkArray[num33].LOD1);
                    this.Map.Write(chunkArray[num33].LOD2);
                    this.Map.Write(chunkArray[num33].LOD3);
                    this.Map.Write(new byte[numArray[num33]]);
                }
            }
            info.ChangeOffset(metaStart + dataSize, ((((this.Map.SecondaryMagic + num) + num2) + num3) + num5) + num20);
            this.Map.Stream.SetLength(((((((this.Map.Length + num) + num2) + num3) + size) + num12) + num15) + num17);
            Header header = this.Map.Header;
            header.Filesize += ((((num2 + num3) + num) + size) + num5) + num20;
            Header header3 = this.Map.Header;
            header3.NonRawSize += ((num2 + num3) + num) + size;
            Header header4 = this.Map.Header;
            header4.MetaSize += size;
            this.Map.Status = "Fixing Crazy...";
            Header header5 = this.Map.Header;
            header5.StrangeFileStringsOffset += num19;
            this.Map.Status = "Fixing Unicode...";
            UnicodeInfo english = this.Map.Unicode.English;
            english.StringIndexOffset += num19;
            UnicodeInfo info2 = this.Map.Unicode.English;
            info2.StringTableOffset += num19;
            UnicodeInfo chinese = this.Map.Unicode.Chinese;
            chinese.StringIndexOffset += num19;
            UnicodeInfo info4 = this.Map.Unicode.Chinese;
            info4.StringTableOffset += num19;
            UnicodeInfo dutch = this.Map.Unicode.Dutch;
            dutch.StringIndexOffset += num19;
            UnicodeInfo info6 = this.Map.Unicode.Dutch;
            info6.StringTableOffset += num19;
            UnicodeInfo french = this.Map.Unicode.French;
            french.StringIndexOffset += num19;
            UnicodeInfo info8 = this.Map.Unicode.French;
            info8.StringTableOffset += num19;
            UnicodeInfo italian = this.Map.Unicode.Italian;
            italian.StringIndexOffset += num19;
            UnicodeInfo info10 = this.Map.Unicode.Italian;
            info10.StringTableOffset += num19;
            UnicodeInfo japanese = this.Map.Unicode.Japanese;
            japanese.StringIndexOffset += num19;
            UnicodeInfo info12 = this.Map.Unicode.Japanese;
            info12.StringTableOffset += num19;
            UnicodeInfo korean = this.Map.Unicode.Korean;
            korean.StringIndexOffset += num19;
            UnicodeInfo info14 = this.Map.Unicode.Korean;
            info14.StringTableOffset += num19;
            UnicodeInfo portuguese = this.Map.Unicode.Portuguese;
            portuguese.StringIndexOffset += num19;
            UnicodeInfo info16 = this.Map.Unicode.Portuguese;
            info16.StringTableOffset += num19;
            UnicodeInfo spanish = this.Map.Unicode.Spanish;
            spanish.StringIndexOffset += num19;
            UnicodeInfo info18 = this.Map.Unicode.Spanish;
            info18.StringTableOffset += num19;
            this.Map.Status = "Moving Data...";
            indexOffset = this.Map.Index.TagInfoIndexOffset + (num * (this.Map.Index.TagCount - 1));
            this.Map.WriteAt(indexOffset + num, this.Map.ReadBytesAt(indexOffset, ((int) this.Map.Length) - indexOffset, true), true);
            this.Map.Status = "Writing Tag Info...";
            this.Map.Position = indexOffset;
            this.Map.WriteReverseString(Class);
            this.Map.Write(info.ID);
            this.Map.Write(info.RawMetaOffset);
            this.Map.Write(info.Size);
            this.Map.Status = "Moving Data...";
            int offset = this.Map.Header.FileTableIndexOffset + ((this.Map.Header.FileTableCount - 1) * num3);
            this.Map.WriteAt((offset + num3) + num15, this.Map.ReadBytesAt(offset, ((int) this.Map.Length) - offset, true), true);
            this.Map.Status = "Fixing Index...";
            Header header6 = this.Map.Header;
            header6.IndexOffset += num3 + num15;
            Header header7 = this.Map.Header;
            header7.IndexSize += num;
            Index index = this.Map.Index;
            index.TagCount++;
            string str = this.PathCache[this.ClassCache.Count - 1];
            indexOffset = (this.Map.Header.FileTableOffset + this.Map.Header.FileTableSize) - (str.Length + 1);
            this.Map.WriteAt((indexOffset + num2) + num12, this.Map.ReadBytesAt(indexOffset, ((int) this.Map.Length) - indexOffset, true), true);
            Header header8 = this.Map.Header;
            header8.FileTableIndexOffset += num2 + num12;
            Header header9 = this.Map.Header;
            header9.IndexOffset += num2 + num12;
            this.Map.Status = "Writing File Path...";
            this.Map.Position = indexOffset;
            int num35 = ((int) this.Map.Position) - this.Map.Header.FileTableOffset;
            this.Map.Write(Path + '\0');
            this.Map.Position += str.Length + 1;
            this.Map.Write(new byte[num11]);
            Header header10 = this.Map.Header;
            header10.FileTableSize += num2;
            this.Map.Status = "Writing File Index...";
            this.Map.Position = this.Map.Header.FileTableIndexOffset + ((this.Map.Header.FileTableCount - 1) * num3);
            this.Map.Write(num35);
            this.Map.Write((int) (num35 + num2));
            this.Map.Write(new byte[num14]);
            Header header11 = this.Map.Header;
            header11.FileTableCount++;
            this.Map.Flush();
            this.Map.Reload();
            this.Map.Status = "Writing Tag...";
            indexOffset = this.Map.Tags.TagInfoCache[this.Map.Tags.TagInfoCache.Count - 2].Offset;
            this.Map.Position = indexOffset;
            int eOFMainMeta = indexOffset + this.GetSize(tag, false);
            this.Map.Write(tag.GetType(), tag, this.Map.Index.SecondaryMagic, ref eOFMainMeta);
            this.Map.Position = eOFMainMeta;
            this.Map.Write(new byte[num16]);
            this.Map.Reload();
            this.Map.Status = "Tag Added Successfully.";
        }

        public void Add(byte[] MetaData, byte[] RawData, string Path, string Class)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        internal static object BlockFromStream(MapStream map, int Offset, System.Type t, int magic, bool retaincurrentposition)
        {
            long position = map.Position;
            map.Position = Offset;
            object obj2 = t.InvokeMember(null, BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, null, new object[0]);
            FieldInfo field = t.GetField("__StartOffset__", BindingFlags.NonPublic | BindingFlags.Instance);
            if (field != null)
            {
                field.SetValue(obj2, Offset);
            }
            foreach (FieldInfo info2 in t.GetFields(BindingFlags.Public | BindingFlags.Instance))
            {
                System.Type fieldType = info2.FieldType;
                if (fieldType.IsArray)
                {
                    if (fieldType == typeof(byte[]))
                    {
                        info2.SetValue(obj2, map.ReadBytes(((byte[]) info2.GetValue(obj2)).Length));
                    }
                    else if (fieldType == typeof(char[]))
                    {
                        info2.SetValue(obj2, map.ReadChars(((char[]) info2.GetValue(obj2)).Length));
                    }
                    else
                    {
                        fieldType = fieldType.GetElementType();
                        int length = map.ReadInt32();
                        int offset = map.ReadInt32() - magic;
                        Array array = Array.CreateInstance(fieldType, length);
                        long num4 = map.Position;
                        for (int i = 0; i < array.Length; i++)
                        {
                            array.SetValue(BlockFromStream(map, offset, fieldType, magic, false), i);
                            offset = (int) map.Position;
                        }
                        info2.SetValue(obj2, array);
                        map.Position = num4;
                    }
                }
                else if (fieldType == typeof(byte))
                {
                    info2.SetValue(obj2, map.ReadByte());
                }
                else if (fieldType == typeof(float))
                {
                    info2.SetValue(obj2, map.ReadFloat());
                }
                else if (fieldType == typeof(short))
                {
                    info2.SetValue(obj2, map.ReadInt16());
                }
                else if (fieldType == typeof(int))
                {
                    info2.SetValue(obj2, map.ReadInt32());
                }
                else if (fieldType == typeof(long))
                {
                    info2.SetValue(obj2, map.ReadInt64());
                }
                else if (fieldType == typeof(ushort))
                {
                    info2.SetValue(obj2, map.ReadUInt16());
                }
                else if (fieldType == typeof(uint))
                {
                    info2.SetValue(obj2, map.ReadUInt32());
                }
                else if (fieldType == typeof(ulong))
                {
                    info2.SetValue(obj2, map.ReadUInt64());
                }
                else if (fieldType == typeof(H2.DataTypes.String))
                {
                    info2.SetValue(obj2, map.ReadFixedLengthString(((H2.DataTypes.String) info2.GetValue(obj2)).Length, false));
                }
                else if (fieldType == typeof(Bitmask))
                {
                    Bitmask bitmask = (Bitmask) info2.GetValue(obj2);
                    if (bitmask.ByteCountLength == 1)
                    {
                        bitmask.SetAllData(map.ReadBytes(1));
                    }
                    else if (bitmask.ByteCountLength == 2)
                    {
                        bitmask.SetAllData(map.ReadBytes(2));
                    }
                    else if (bitmask.ByteCountLength == 4)
                    {
                        byte[] bytes = map.ReadBytes(4);
                        bitmask.SetAllData(bytes);
                    }
                    info2.SetValue(obj2, bitmask);
                }
                else if (fieldType == typeof(Dependancy))
                {
                    info2.SetValue(obj2, map.ReadDependancy());
                }
                else if (fieldType == typeof(StringID))
                {
                    StringID gid = map.ReadStringID();
                    info2.SetValue(obj2, gid);
                }
                else
                {
                    if (fieldType != typeof(H2.DataTypes.Enum))
                    {
                        throw new Exception("Unknown Type");
                    }
                    H2.DataTypes.Enum enum2 = (H2.DataTypes.Enum) info2.GetValue(obj2);
                    if (enum2.ByteCountLength == 1)
                    {
                        enum2.Value = map.ReadByte();
                    }
                    else if (enum2.ByteCountLength == 2)
                    {
                        enum2.Value = map.ReadInt16();
                    }
                    else if (enum2.ByteCountLength == 4)
                    {
                        enum2.Value = map.ReadInt32();
                    }
                    info2.SetValue(obj2, enum2);
                }
            }
            if (retaincurrentposition)
            {
                map.Position = position;
            }
            return obj2;
        }

        public void Clear()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public bool Contains(string Class, string Path)
        {
            for (int i = 0; i < this.PathCache.Count; i++)
            {
                if ((this.PathCache[i] == Path) && (this.ClassCache[i] == Class))
                {
                    return true;
                }
            }
            return false;
        }

        private void CreateStructureCache()
        {
            System.Type[] nestedTypes = typeof(TagStructures).GetNestedTypes();
            this.TagStructureTypeDic = new Dictionary<string, System.Type>(nestedTypes.Length);
            for (int i = 0; i < nestedTypes.Length; i++)
            {
                object[] attributes = nestedTypes[i].GetCustomAttributes(typeof(H2.TagStructures.Tag), false);
                if(attributes.Length < 1) continue;
                this.TagStructureTypeDic.Add(((H2.TagStructures.Tag)attributes[0]).Name, nestedTypes[i]);
            }
        }

        private void CreateTagCache()
        {
            long position = this.Map.Position;
            int fileTableCount = this.Map.Header.FileTableCount;
            this.Map.Position = this.Map.FileTableOffset;
            this.PathCache = new List<string>(fileTableCount);
            this.ClassCache = new List<string>(fileTableCount);
            this.TagInfoCache = new List<TagInfo>(fileTableCount);
            char[] separator = new char[1];
            this.PathCache.AddRange(new string(this.Map.ReadChars(this.Map.Header.FileTableSize)).Split(separator, StringSplitOptions.RemoveEmptyEntries));
            int scnrID = this.Map.Index.ScnrID;
            this.Map.Position = this.Map.TagInfoIndexOffset;
            for (int i = 0; i < fileTableCount; i++)
            {
                this.ClassCache.Add(this.Map.ReadReverseString(4, false));
                this.TagInfoCache.Add(new TagInfo());
                this.TagInfoCache[i].ID = this.Map.ReadInt32();
                this.TagInfoCache[i].ChangeOffset(this.Map.ReadInt32() - this.Map.SecondaryMagic, this.Map.SecondaryMagic);
                this.TagInfoCache[i].Size = this.Map.ReadInt32();
                if (this.TagInfoCache[i].ID == scnrID)
                {
                    this.Map.Scnr = this.TagInfoCache[i];
                }
            }
            this.Map.Position = position;
        }

        public void Duplicate(string Class, string Path, string NewTagPath)
        {
            TagContainer tagCon = new TagContainer();
            tagCon.Tag = this.LoadTag(Class, Path);
            tagCon.RawData = this.GetRaw(tagCon.Tag, Class, null, null, null);
            this.Add(tagCon, Class, NewTagPath);
        }

        public TagContainer ExtractTag(int i, SearchType st, BinaryReader MainMenu, BinaryReader Shared, BinaryReader SpShared)
        {
            TagContainer container = new TagContainer();
            container.Tag = this.LoadTag(i, st);
            container.RawData = this.GetRaw(container.Tag, container.Tag.GetType().Name, MainMenu, Shared, SpShared);
            return container;
        }

        public TagContainer ExtractTag(string Class, string Path, BinaryReader MainMenu, BinaryReader Shared, BinaryReader SpShared)
        {
            TagContainer container = new TagContainer();
            container.Tag = this.LoadTag(Class, Path);
            container.RawData = this.GetRaw(container.Tag, Class, MainMenu, Shared, SpShared);
            return container;
        }

        public void Flush()
        {
            long position = this.Map.Position;
            int fileTableCount = this.Map.Header.FileTableCount;
            this.Map.Position = this.Map.FileTableOffset;
            foreach (string str in this.PathCache)
            {
                this.Map.Write(str.ToCharArray());
                this.Map.Write('0');
            }
            this.Map.Position = this.Map.TagInfoIndexOffset;
            for (int i = 0; i < fileTableCount; i++)
            {
                this.Map.Write(this.ClassCache[i].ToCharArray(0, 4));
                this.Map.Write(this.TagInfoCache[i].ID);
                this.Map.Write(this.TagInfoCache[i].RawMetaOffset);
                this.Map.Write(this.TagInfoCache[i].Size);
            }
            this.Map.Position = position;
        }

        private object[] GetRaw(object Tag, string Class, BinaryReader MainMenu, BinaryReader Shared, BinaryReader SpShared)
        {
            switch (Class)
            {
                case "bitm":
                {
                    TagStructures.bitm._68_bitmap_data[] _dataArray = ((TagStructures.bitm) Tag)._68_Bitmap_Data;
                    this.Map.Status = "Getting Bitmap Data...";
                    BitmapCollection.BitmapDataChunk[] chunkArray = new BitmapCollection.BitmapDataChunk[_dataArray.Length];
                    int num = _dataArray[0]._28_LOD1_Offset;
                    BinaryReader reader = null;
                    long num2 = num & ((long) 0xc0000000L);
                    num &= 0x3fffffff;
                    switch (num2)
                    {
                        case 0x80000000L:
                            reader = Shared;
                            break;

                        case 0xc0000000L:
                            reader = SpShared;
                            break;

                        case 0L:
                            reader = new BinaryReader(this.Map.Stream);
                            break;

                        case 0x40000000L:
                            reader = MainMenu;
                            break;
                    }
                    if (reader == null)
                    {
                        return new BitmapCollection.BitmapDataChunk[0];
                    }
                    for (int i = 0; i < _dataArray.Length; i++)
                    {
                        chunkArray[i] = new BitmapCollection.BitmapDataChunk();
                        reader.BaseStream.Position = _dataArray[i]._28_LOD1_Offset & 0x3fffffff;
                        chunkArray[i].LOD1 = reader.ReadBytes(_dataArray[i]._52_LOD1_Size);
                        reader.BaseStream.Position = _dataArray[i]._32_LOD2_Offset & 0x3fffffff;
                        chunkArray[i].LOD2 = reader.ReadBytes(_dataArray[i]._56_LOD2_Size);
                        reader.BaseStream.Position = _dataArray[i]._36_LOD3_Offset & 0x3fffffff;
                        chunkArray[i].LOD3 = reader.ReadBytes(_dataArray[i]._60_LOD3_Size);
                    }
                    return chunkArray;
                }
                case "mode":
                    return new object[0];

                case "snd_":
                    return new object[0];
            }
            return new object[0];
        }

        public int GetSize(object Tag, bool Recursive)
        {
            int num = 0;
            foreach (FieldInfo info in Tag.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance))
            {
                System.Type fieldType = info.FieldType;
                if (fieldType.IsArray)
                {
                    if (fieldType == typeof(byte[]))
                    {
                        num += ((byte[]) info.GetValue(Tag)).Length;
                    }
                    else if (fieldType == typeof(char[]))
                    {
                        num += ((char[]) info.GetValue(Tag)).Length;
                    }
                    else if (Recursive)
                    {
                        Array array = (Array) info.GetValue(Tag);
                        num += 8;
                        for (int i = 0; i < array.Length; i++)
                        {
                            num += this.GetSize(array.GetValue(i), Recursive);
                        }
                    }
                    else
                    {
                        num += 8;
                    }
                }
                else if (fieldType == typeof(byte))
                {
                    num++;
                }
                else if (fieldType == typeof(float))
                {
                    num += 4;
                }
                else if (fieldType == typeof(short))
                {
                    num += 2;
                }
                else if (fieldType == typeof(int))
                {
                    num += 4;
                }
                else if (fieldType == typeof(long))
                {
                    num += 8;
                }
                else if (fieldType == typeof(ushort))
                {
                    num += 2;
                }
                else if (fieldType == typeof(uint))
                {
                    num += 4;
                }
                else if (fieldType == typeof(ulong))
                {
                    num += 8;
                }
                else if (fieldType == typeof(H2.DataTypes.String))
                {
                    num += (int)((H2.DataTypes.String) info.GetValue(Tag)).Length;
                }
                else if (fieldType == typeof(Bitmask))
                {
                    num += ((Bitmask) info.GetValue(Tag)).ByteCountLength;
                }
                else if (fieldType == typeof(Dependancy))
                {
                    num += 8;
                }
                else if (fieldType == typeof(StringID))
                {
                    num += 4;
                }
                else
                {
                    if (fieldType != typeof(H2.DataTypes.Enum))
                    {
                        throw new Exception("Unknown Type");
                    }
                    num += ((H2.DataTypes.Enum) info.GetValue(Tag)).ByteCountLength;
                }
            }
            return num;
        }

        public string[] GetStringArrayOfClass(string Class)
        {
            int count = this.TagInfoCache.Count;
            List<string> list = new List<string>();
            for (int i = 0; i < count; i++)
            {
                if (this.ClassCache[i] == Class)
                {
                    list.Add(this.PathCache[i]);
                }
            }
            return list.ToArray();
        }

        public string[] GetStringArrayOfClasses()
        {
            List<string> list = new List<string>(this.ClassCache.Count);
            foreach (string str in this.ClassCache)
            {
                if (!list.Contains(str))
                {
                    list.Add(str);
                }
            }
            return list.ToArray();
        }

        public TagInfo[] GetTagInfoArrayOfClass(string Class)
        {
            int count = this.TagInfoCache.Count;
            List<TagInfo> list = new List<TagInfo>();
            for (int i = 0; i < count; i++)
            {
                if (this.ClassCache[i] == Class)
                {
                    list.Add(this.TagInfoCache[i]);
                }
            }
            return list.ToArray();
        }

        public string GetTagPath(int i, SearchType st)
        {
            if (st == SearchType.Index)
            {
                return this.PathCache[i];
            }
            for (int j = 0; j < this.TagInfoCache.Count; j++)
            {
                if (this.TagInfoCache[j].ID == i)
                {
                    return this.PathCache[j];
                }
            }
            return "";
        }

        public TagTable GetTagTable()
        {
            this.Map.Status = "Creating Tag Table";
            TagTable table = new TagTable(this.ClassCache.Count);
            for (int i = 0; i < this.ClassCache.Count; i++)
            {
                table.Add(new TagTable.TagRef(this.ClassCache[i], this.PathCache[i], this.TagInfoCache[i].Offset, this.TagInfoCache[i].Size, this.TagInfoCache[i].ID));
            }
            table.Sort();
            this.Map.Status = "Ready.";
            return table;
        }

        public int GetUnusedID()
        {
            int iD = this.TagInfoCache[0].ID;
            foreach (TagInfo info in this.TagInfoCache)
            {
                if (info.ID > iD)
                {
                    iD = info.ID;
                }
            }
            return (iD + 0x10001);
        }

        public int IndexOf(string Class, string Path)
        {
            for (int i = 0; i < this.PathCache.Count; i++)
            {
                if ((this.PathCache[i] == Path) && (this.ClassCache[i] == Class))
                {
                    return i;
                }
            }
            return -1;
        }

        public object LoadTag(int i, SearchType st)
        {
            if (st == SearchType.Index)
            {
                int primaryMagic;
                this.Map.Status = "Loading Tag " + this.ClassCache[i] + " - " + this.PathCache[i];
                Application.DoEvents();
                System.Type t = this.TagStructureTypeDic[this.ClassCache[i]];
                if (t == null)
                {
                    return null;
                }
                if (this.ClassCache[i] == "sbsp")
                {
                    primaryMagic = this.Map.Index.PrimaryMagic;
                }
                else
                {
                    primaryMagic = this.Map.SecondaryMagic;
                }
                return BlockFromStream(this.Map, this.TagInfoCache[i].ID, t, primaryMagic, true);
            }
            for (int j = 0; j < this.TagInfoCache.Count; j++)
            {
                if (this.TagInfoCache[j].ID == i)
                {
                    int secondaryMagic;
                    this.Map.Status = "Loading Tag " + this.ClassCache[j] + " - " + this.PathCache[j];
                    Application.DoEvents();
                    System.Type type2 = this.TagStructureTypeDic[this.ClassCache[j]];
                    if (type2 == null)
                    {
                        return null;
                    }
                    if (this.ClassCache[j] == "sbsp")
                    {
                        secondaryMagic = this.Map.Index.PrimaryMagic;
                    }
                    else
                    {
                        secondaryMagic = this.Map.SecondaryMagic;
                    }
                    return BlockFromStream(this.Map, this.TagInfoCache[j].Offset, type2, secondaryMagic, true);
                }
            }
            return null;
        }

        public object LoadTag(string Class, string Path)
        {
            int magic;
            this.Map.Status = "Loading Tag " + Class + " - " + Path;
            Application.DoEvents();
            System.Type t = this.TagStructureTypeDic[Class];
            if (t == null)
            {
                return null;
            }
            if (Class == "sbsp")
            {
                magic = this.Map.Sbsp[Path].Magic;
            }
            else
            {
                magic = this.Map.SecondaryMagic;
            }
            return BlockFromStream(this.Map, this[Class, Path].Offset, t, magic, true);
        }

        public void ReClass(int i, SearchType st, string newClass)
        {
            if (st == SearchType.Index)
            {
                if (newClass.Length < 4)
                {
                    for (int j = newClass.Length; j < 5; j++)
                    {
                        newClass = newClass + '\0';
                    }
                }
                else if (newClass.Length > 4)
                {
                    newClass = newClass.Substring(0, 4);
                }
                this.ClassCache[i] = newClass;
                long position = this.Map.Position;
                this.Map.Position = this.Map.TagInfoIndexOffset + (0x10 * i);
                this.Map.WriteReverseString(newClass);
                this.Map.Position = position;
            }
            else
            {
                for (int k = 0; k < this.TagInfoCache.Count; k++)
                {
                    if (this.TagInfoCache[k].ID == i)
                    {
                        if (newClass.Length < 4)
                        {
                            for (int m = newClass.Length; m < 5; m++)
                            {
                                newClass = newClass + '\0';
                            }
                        }
                        else if (newClass.Length > 4)
                        {
                            newClass = newClass.Substring(0, 4);
                        }
                        this.ClassCache[k] = newClass;
                        long num5 = this.Map.Position;
                        this.Map.Position = this.Map.TagInfoIndexOffset + (0x10 * k);
                        this.Map.WriteReverseString(newClass);
                        this.Map.Position = num5;
                    }
                }
            }
        }

        public void ReClass(string OldClass, string Path, string NewClass)
        {
            for (int i = 0; i < this.PathCache.Count; i++)
            {
                if ((this.PathCache[i] == Path) && (this.ClassCache[i] == OldClass))
                {
                    if (NewClass.Length < 4)
                    {
                        for (int j = NewClass.Length; j < 5; j++)
                        {
                            NewClass = NewClass + '\0';
                        }
                    }
                    else if (NewClass.Length > 4)
                    {
                        NewClass = NewClass.Substring(0, 4);
                    }
                    this.ClassCache[i] = NewClass;
                    long position = this.Map.Position;
                    this.Map.Position = this.Map.TagInfoIndexOffset + (0x10 * i);
                    this.Map.WriteReverseString(NewClass);
                    this.Map.Position = position;
                }
            }
        }

        public bool Remove(string Path, string Class)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        public string Rename(int i, SearchType st, string newPath)
        {
            if (st == SearchType.Index)
            {
                if (this.PathCache[i].Length > newPath.Length)
                {
                    int num = this.PathCache[i].Length - newPath.Length;
                    for (int m = 0; m < num; m++)
                    {
                        newPath = newPath + ' ';
                    }
                }
                else if (this.PathCache[i].Length < newPath.Length)
                {
                    newPath = newPath.Substring(0, this.PathCache[i].Length);
                }
                this.PathCache[i] = newPath;
                long position = this.Map.Position;
                this.Map.Position = this.Map.FileTableOffset;
                for (int k = 0; k < i; k++)
                {
                    this.Map.ReadNullTerminatedString();
                }
                this.Map.Write(newPath.ToCharArray());
                this.Map.Position = position;
                return newPath;
            }
            for (int j = 0; j < this.TagInfoCache.Count; j++)
            {
                if (this.TagInfoCache[j].ID == i)
                {
                    if (this.PathCache[i].Length > newPath.Length)
                    {
                        int num6 = this.PathCache[i].Length - newPath.Length;
                        for (int num7 = 0; num7 < num6; num7++)
                        {
                            newPath = newPath + ' ';
                        }
                    }
                    else if (this.PathCache[i].Length < newPath.Length)
                    {
                        newPath = newPath.Substring(0, this.PathCache[i].Length);
                    }
                    this.PathCache[i] = newPath;
                    long num8 = this.Map.Position;
                    this.Map.Position = this.Map.FileTableOffset;
                    for (int n = 0; n < i; n++)
                    {
                        this.Map.ReadNullTerminatedString();
                    }
                    this.Map.Write(newPath.ToCharArray());
                    this.Map.Position = num8;
                    return newPath;
                }
            }
            return newPath;
        }

        public string Rename(string Class, string OldPath, string NewPath)
        {
            for (int i = 0; i < this.PathCache.Count; i++)
            {
                if ((this.PathCache[i] == OldPath) && (this.ClassCache[i] == Class))
                {
                    if (this.PathCache[i].Length > NewPath.Length)
                    {
                        int num2 = this.PathCache[i].Length - NewPath.Length;
                        for (int k = 0; k < num2; k++)
                        {
                            NewPath = NewPath + ' ';
                        }
                    }
                    else if (this.PathCache[i].Length < NewPath.Length)
                    {
                        NewPath = NewPath.Substring(0, this.PathCache[i].Length);
                    }
                    this.PathCache[i] = NewPath;
                    long position = this.Map.Position;
                    this.Map.Position = this.Map.FileTableOffset;
                    for (int j = 0; j < i; j++)
                    {
                        this.Map.ReadNullTerminatedString();
                    }
                    this.Map.Write(NewPath.ToCharArray());
                    this.Map.Position = position;
                    return NewPath;
                }
            }
            return NewPath;
        }

        public int Count
        {
            get
            {
                return this.Map.Index.TagCount;
            }
        }

        public TagInfo this[int i, SearchType st]
        {
            get
            {
                if (st == SearchType.Index)
                {
                    return this.TagInfoCache[i];
                }
                foreach (TagInfo info in this.TagInfoCache)
                {
                    if (info.ID == i)
                    {
                        return info;
                    }
                }
                return null;
            }
            set
            {
                if (st == SearchType.Index)
                {
                    this.TagInfoCache[i] = value;
                    long position = this.Map.Position;
                    this.Map.Position = this.Map.Header.FileTableIndexOffset + (i * 12);
                    this.Map.Write(this.TagInfoCache[i].ID);
                    this.Map.Write(this.TagInfoCache[i].RawMetaOffset);
                    this.Map.Write(this.TagInfoCache[i].Size);
                    this.Map.Position = position;
                }
                else
                {
                    for (int j = 0; j < this.TagInfoCache.Count; j++)
                    {
                        if (this.TagInfoCache[j].ID == i)
                        {
                            this.TagInfoCache[j] = value;
                            long num3 = this.Map.Position;
                            this.Map.Position = this.Map.Header.FileTableIndexOffset + (i * 12);
                            this.Map.Write(this.TagInfoCache[j].ID);
                            this.Map.Write(this.TagInfoCache[j].RawMetaOffset);
                            this.Map.Write(this.TagInfoCache[j].Size);
                            this.Map.Position = num3;
                            return;
                        }
                    }
                }
            }
        }

        public TagInfo this[string Class, string Path]
        {
            get
            {
                for (int i = 0; i < this.PathCache.Count; i++)
                {
                    if ((this.PathCache[i] == Path) && (this.ClassCache[i] == Class))
                    {
                        return this.TagInfoCache[i];
                    }
                }
                return null;
            }
            set
            {
                for (int i = 0; i < this.PathCache.Count; i++)
                {
                    if ((this.PathCache[i] == Path) && (this.ClassCache[i] == Class))
                    {
                        this.TagInfoCache[i] = value;
                        return;
                    }
                }
            }
        }

        public enum SearchType
        {
            Index,
            ID
        }

        public class TagTable
        {
            private List<TagRef> Tags;

            internal TagTable(int Capacity)
            {
                this.Tags = new List<TagRef>(Capacity);
            }

            internal void Add(TagRef tr)
            {
                this.Tags.Add(tr);
            }

            public void Sort()
            {
                this.Tags.Sort();
            }

            public int Count
            {
                get
                {
                    return this.Tags.Count;
                }
            }

            public TagRef this[int index]
            {
                get
                {
                    return this.Tags[index];
                }
                set
                {
                    this.Tags[index] = value;
                }
            }

            public class TagRef : IComparable<Tags.TagTable.TagRef>
            {
                public string Class;
                public int ID;
                public int Offset;
                public string Path;
                public int Size;

                internal TagRef(string _class, string _path, int _offset, int _size, int _id)
                {
                    this.Class = _class;
                    this.Path = _path;
                    this.Offset = _offset;
                    this.Size = _size;
                    this.ID = _id;
                }

                public int CompareTo(Tags.TagTable.TagRef other)
                {
                    if (this.Class != other.Class)
                    {
                        return this.Class.CompareTo(other.Class);
                    }
                    return this.Path.CompareTo(other.Path);
                }
            }
        }
    }
}

